"""
接口自动化服务模块
负责接口调用、场景执行、断言验证等功能
"""

import json
import time
import asyncio
import logging
import uuid
from typing import Dict, Any, List, Optional, Tuple
from datetime import datetime
import httpx
import jsonpath_ng
from sqlalchemy.orm import Session

from ..models.api_automation import (
    ApiInterface, ApiParameter, ApiAssertion, TestScenario,
    ScenarioExecution, StepExecutionResult, EnvironmentConfig
)
from ..schemas.api_automation import (
    ApiTestRequest, ApiTestResponse, ScenarioExecuteRequest, 
    ScenarioExecuteResponse, ScenarioDebugRequest
)

logger = logging.getLogger(__name__)


class ApiAutomationService:
    """接口自动化服务类"""
    
    def __init__(self):
        self.http_client = httpx.AsyncClient(
            timeout=httpx.Timeout(300.0),  # 设置超时时间为5分钟
            limits=httpx.Limits(max_keepalive_connections=20, max_connections=100)
        )
        self.execution_contexts = {}  # 存储执行上下文
    
    async def close(self):
        """关闭HTTP客户端"""
        await self.http_client.aclose()
    
    def _build_request_url(self, interface: ApiInterface, environment: Optional[EnvironmentConfig] = None, 
                          variables: Optional[Dict[str, str]] = None) -> str:
        """构建请求URL"""
        url = interface.url
        
        # 替换环境变量
        if environment and environment.base_url:
            if url.startswith('/'):
                url = environment.base_url.rstrip('/') + url
            elif not url.startswith('http'):
                url = environment.base_url.rstrip('/') + '/' + url.lstrip('/')
        
        # 替换变量
        if variables:
            for key, value in variables.items():
                url = url.replace(f"{{{key}}}", str(value))
                url = url.replace(f"{{{{key}}}}", str(value))
        
        return url
    
    def _build_request_headers(self, interface: ApiInterface, environment: Optional[EnvironmentConfig] = None,
                              parameter_overrides: Optional[Dict[str, Any]] = None,
                              variables: Optional[Dict[str, str]] = None) -> Dict[str, str]:
        """构建请求头"""
        headers = {}
        
        # 环境全局头
        if environment and environment.headers:
            headers.update(environment.headers)
        
        # 接口默认头
        if interface.headers:
            headers.update(interface.headers)
        
        # 参数中的头
        for param in interface.parameters:
            if param.param_type == "header":
                value = self._get_parameter_value(param, parameter_overrides, variables)
                if value is not None:
                    headers[param.name] = str(value)
        
        # 处理前端传来的header参数（格式：header.key）
        if parameter_overrides:
            for key, value in parameter_overrides.items():
                if key.startswith("header.") and value is not None:
                    header_name = key[7:]  # 移除"header."前缀
                    headers[header_name] = str(value)
        
        # 认证头
        if interface.auth_type and interface.auth_config:
            auth_header = self._build_auth_header(interface.auth_type, interface.auth_config, variables)
            if auth_header:
                headers.update(auth_header)
        
        return headers
    
    def _build_auth_header(self, auth_type: str, auth_config: Dict[str, Any], 
                          variables: Optional[Dict[str, str]] = None) -> Optional[Dict[str, str]]:
        """构建认证头"""
        if auth_type == "bearer":
            token = auth_config.get("token", "")
            if variables and token.startswith("{{") and token.endswith("}}"):
                var_name = token[2:-2]
                token = variables.get(var_name, token)
            return {"Authorization": f"Bearer {token}"}
        
        elif auth_type == "basic":
            username = auth_config.get("username", "")
            password = auth_config.get("password", "")
            if variables:
                if username.startswith("{{") and username.endswith("}}"):
                    var_name = username[2:-2]
                    username = variables.get(var_name, username)
                if password.startswith("{{") and password.endswith("}}"):
                    var_name = password[2:-2]
                    password = variables.get(var_name, password)
            
            import base64
            credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
            return {"Authorization": f"Basic {credentials}"}
        
        elif auth_type == "api_key":
            key = auth_config.get("key", "")
            value = auth_config.get("value", "")
            if variables and value.startswith("{{") and value.endswith("}}"):
                var_name = value[2:-2]
                value = variables.get(var_name, value)
            return {key: value}
        
        return None
    
    def _build_query_params(self, interface: ApiInterface, parameter_overrides: Optional[Dict[str, Any]] = None,
                           variables: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
        """构建查询参数"""
        params = {}
        
        # 接口默认查询参数
        if interface.query_params:
            params.update(interface.query_params)
        
        # 参数中的查询参数
        for param in interface.parameters:
            if param.param_type == "query":
                value = self._get_parameter_value(param, parameter_overrides, variables)
                if value is not None:
                    params[param.name] = value
        
        # 处理前端传来的query参数（格式：query.key）
        if parameter_overrides:
            for key, value in parameter_overrides.items():
                if key.startswith("query.") and value is not None:
                    param_name = key[6:]  # 移除"query."前缀
                    params[param_name] = value
        
        return params
    
    def _build_request_body(self, interface: ApiInterface, parameter_overrides: Optional[Dict[str, Any]] = None,
                           variables: Optional[Dict[str, str]] = None) -> Optional[Any]:
        """构建请求体"""
        # 首先检查前端传来的body参数覆盖
        if parameter_overrides:
            # 处理JSON body
            if "body.json" in parameter_overrides:
                try:
                    return json.loads(parameter_overrides["body.json"])
                except json.JSONDecodeError:
                    logger.warning(f"Invalid JSON body from frontend: {parameter_overrides['body.json']}")
                    return parameter_overrides["body.json"]
            
            # 处理Form body
            elif "body.form" in parameter_overrides:
                try:
                    return json.loads(parameter_overrides["body.form"])
                except json.JSONDecodeError:
                    logger.warning(f"Invalid form data from frontend: {parameter_overrides['body.form']}")
                    return {}
            
            # 处理Raw body
            elif "body.raw" in parameter_overrides:
                return parameter_overrides["body.raw"]
        
        # 如果没有前端覆盖，使用原有逻辑
        if interface.body_type == "none" or not interface.body_content:
            return None
        
        body_content = interface.body_content
        
        # 替换变量
        if variables:
            for key, value in variables.items():
                body_content = body_content.replace(f"{{{key}}}", str(value))
                body_content = body_content.replace(f"{{{{key}}}}", str(value))
        
        # 处理body类型参数的覆盖
        if interface.body_type == "json":
            try:
                body_data = json.loads(body_content) if body_content else {}
                
                # 应用body参数
                for param in interface.parameters:
                    if param.param_type == "body":
                        value = self._get_parameter_value(param, parameter_overrides, variables)
                        if value is not None:
                            # 支持嵌套路径，如 "user.name"
                            keys = param.name.split('.')
                            current = body_data
                            for key in keys[:-1]:
                                if key not in current:
                                    current[key] = {}
                                current = current[key]
                            current[keys[-1]] = value
                
                return body_data
            except json.JSONDecodeError:
                logger.warning(f"Invalid JSON body content: {body_content}")
                return body_content
        
        elif interface.body_type == "form":
            form_data = {}
            
            # 解析表单数据
            if body_content:
                for line in body_content.split('\n'):
                    if '=' in line:
                        key, value = line.split('=', 1)
                        form_data[key.strip()] = value.strip()
            
            # 应用body参数
            for param in interface.parameters:
                if param.param_type == "body":
                    value = self._get_parameter_value(param, parameter_overrides, variables)
                    if value is not None:
                        form_data[param.name] = value
            
            return form_data
        
        return body_content
    
    def _get_parameter_value(self, param: ApiParameter, parameter_overrides: Optional[Dict[str, Any]] = None,
                            variables: Optional[Dict[str, str]] = None) -> Any:
        """获取参数值"""
        # 优先使用覆盖值
        if parameter_overrides and param.name in parameter_overrides:
            return parameter_overrides[param.name]
        
        # 根据来源类型获取值
        if param.source_type == "variable" and param.source_config:
            var_name = param.source_config.get("variable_name")
            if var_name and variables and var_name in variables:
                return self._convert_value(variables[var_name], param.data_type)
        
        elif param.source_type == "static":
            if param.example_value:
                return self._convert_value(param.example_value, param.data_type)
            elif param.default_value:
                return self._convert_value(param.default_value, param.data_type)
        
        return None
    
    def _convert_value(self, value: str, data_type: str) -> Any:
        """转换值类型"""
        if not value:
            return None
        
        try:
            if data_type == "integer":
                return int(value)
            elif data_type == "number":
                return float(value)
            elif data_type == "boolean":
                return value.lower() in ("true", "1", "yes", "on")
            elif data_type == "array":
                return json.loads(value) if value.startswith('[') else value.split(',')
            elif data_type == "object":
                return json.loads(value)
            else:
                return str(value)
        except (ValueError, json.JSONDecodeError):
            return value
    
    async def test_api_interface(self, request: ApiTestRequest, db: Session) -> ApiTestResponse:
        """测试单个API接口"""
        try:
            # 获取接口信息
            interface = db.query(ApiInterface).filter(ApiInterface.id == request.interface_id).first()
            if not interface:
                return ApiTestResponse(
                    success=False,
                    error_message="接口不存在"
                )
            
            # 获取环境配置
            environment = None
            if request.environment_id:
                environment = db.query(EnvironmentConfig).filter(
                    EnvironmentConfig.id == request.environment_id
                ).first()
            
            # 构建请求
            url = self._build_request_url(interface, environment, request.variables)
            headers = self._build_request_headers(interface, environment, request.parameter_overrides, request.variables)
            params = self._build_query_params(interface, request.parameter_overrides, request.variables)
            body = self._build_request_body(interface, request.parameter_overrides, request.variables)
            
            # 发送请求
            start_time = time.time()
            
            if interface.body_type == "json" and body:
                response = await self.http_client.request(
                    method=interface.method,
                    url=url,
                    headers=headers,
                    params=params,
                    json=body
                )
            elif interface.body_type == "form" and body:
                response = await self.http_client.request(
                    method=interface.method,
                    url=url,
                    headers=headers,
                    params=params,
                    data=body
                )
            else:
                response = await self.http_client.request(
                    method=interface.method,
                    url=url,
                    headers=headers,
                    params=params,
                    content=body if isinstance(body, (str, bytes)) else None
                )
            
            response_time = (time.time() - start_time) * 1000  # 转换为毫秒
            
            # 执行断言
            assertion_results = []
            for assertion in interface.assertions:
                if assertion.is_enabled:
                    result = self._execute_assertion(assertion, response, response_time)
                    assertion_results.append(result)
            
            return ApiTestResponse(
                success=True,
                status_code=response.status_code,
                response_time=response_time,
                response_headers=dict(response.headers),
                response_body=response.text,
                assertion_results=assertion_results
            )
            
        except Exception as e:
            logger.error(f"API接口测试失败: {str(e)}")
            return ApiTestResponse(
                success=False,
                error_message=str(e)
            )
    
    def _execute_assertion(self, assertion: ApiAssertion, response: httpx.Response, 
                          response_time: float) -> Dict[str, Any]:
        """执行断言"""
        result = {
            "name": assertion.name,
            "type": assertion.assertion_type,
            "operator": assertion.operator,
            "expected": assertion.expected_value,
            "actual": None,
            "passed": False,
            "message": ""
        }
        
        try:
            if assertion.assertion_type == "status_code":
                actual_value = response.status_code
                result["actual"] = actual_value
                result["passed"] = self._compare_values(actual_value, assertion.operator, assertion.expected_value)
            
            elif assertion.assertion_type == "response_time":
                actual_value = response_time
                result["actual"] = actual_value
                expected_value = float(assertion.expected_value) if assertion.expected_value else 0
                result["passed"] = self._compare_values(actual_value, assertion.operator, expected_value)
            
            elif assertion.assertion_type == "header":
                header_value = response.headers.get(assertion.target_path, "")
                result["actual"] = header_value
                result["passed"] = self._compare_values(header_value, assertion.operator, assertion.expected_value)
            
            elif assertion.assertion_type == "body_contains":
                body_text = response.text
                result["actual"] = body_text
                result["passed"] = assertion.expected_value in body_text if assertion.expected_value else False
            
            elif assertion.assertion_type == "json_path":
                try:
                    response_json = response.json()
                    jsonpath_expr = jsonpath_ng.parse(assertion.target_path)
                    matches = jsonpath_expr.find(response_json)
                    if matches:
                        actual_value = matches[0].value
                        result["actual"] = actual_value
                        result["passed"] = self._compare_values(actual_value, assertion.operator, assertion.expected_value)
                    else:
                        result["actual"] = None
                        result["passed"] = False
                        result["message"] = f"JSONPath未找到匹配项: {assertion.target_path}"
                except json.JSONDecodeError:
                    result["passed"] = False
                    result["message"] = "响应不是有效的JSON格式"
                except Exception as e:
                    result["passed"] = False
                    result["message"] = f"JSONPath解析错误: {str(e)}"
            
            if result["passed"]:
                result["message"] = "断言通过"
            elif not result["message"]:
                result["message"] = f"断言失败: 期望 {assertion.expected_value}, 实际 {result['actual']}"
                
        except Exception as e:
            result["passed"] = False
            result["message"] = f"断言执行错误: {str(e)}"
        
        return result
    
    def _compare_values(self, actual: Any, operator: str, expected: Any) -> bool:
        """比较值"""
        try:
            if operator == "equals":
                return str(actual) == str(expected)
            elif operator == "not_equals":
                return str(actual) != str(expected)
            elif operator == "contains":
                return str(expected) in str(actual)
            elif operator == "not_contains":
                return str(expected) not in str(actual)
            elif operator == "greater_than":
                return float(actual) > float(expected)
            elif operator == "less_than":
                return float(actual) < float(expected)
            elif operator == "greater_equal":
                return float(actual) >= float(expected)
            elif operator == "less_equal":
                return float(actual) <= float(expected)
            else:
                return False
        except (ValueError, TypeError):
            return False
    
    async def execute_scenario(self, request: ScenarioExecuteRequest, db: Session, 
                              user_id: int) -> ScenarioExecuteResponse:
        """执行测试场景"""
        try:
            # 获取场景信息
            scenario = db.query(TestScenario).filter(TestScenario.id == request.scenario_id).first()
            if not scenario:
                return ScenarioExecuteResponse(
                    execution_id="",
                    status="failed",
                    message="场景不存在"
                )
            
            # 创建执行记录
            execution_id = str(uuid.uuid4())
            execution = ScenarioExecution(
                execution_id=execution_id,
                scenario_id=scenario.id,
                executor_id=user_id,
                status="pending",
                total_steps=len(scenario.steps),
                start_time=datetime.utcnow()
            )
            db.add(execution)
            db.commit()
            
            # 异步执行场景
            if request.debug_mode:
                # 调试模式：同步执行并返回详细结果
                await self._execute_scenario_steps(execution.id, scenario, request, db)
            else:
                # 正常模式：异步执行
                asyncio.create_task(self._execute_scenario_steps(execution.id, scenario, request, db))
            
            return ScenarioExecuteResponse(
                execution_id=execution_id,
                status="running",
                message="场景执行已启动"
            )
            
        except Exception as e:
            logger.error(f"场景执行启动失败: {str(e)}")
            return ScenarioExecuteResponse(
                execution_id="",
                status="failed",
                message=f"执行启动失败: {str(e)}"
            )
    
    async def _execute_scenario_steps(self, execution_id: int, scenario: TestScenario, 
                                     request: ScenarioExecuteRequest, db: Session):
        """执行场景步骤"""
        execution = db.query(ScenarioExecution).filter(ScenarioExecution.id == execution_id).first()
        if not execution:
            return
        
        try:
            # 更新执行状态
            execution.status = "running"
            execution.start_time = datetime.utcnow()
            db.commit()
            
            # 获取环境配置
            environment = None
            if request.environment_id:
                environment = db.query(EnvironmentConfig).filter(
                    EnvironmentConfig.id == request.environment_id
                ).first()
            
            # 初始化执行上下文
            context = {
                "variables": request.variables.copy() if request.variables else {},
                "environment": environment,
                "scenario": scenario
            }
            
            # 添加环境变量
            if environment and environment.variables:
                context["variables"].update(environment.variables)
            
            # 添加场景环境变量
            if scenario.environment_variables:
                context["variables"].update(scenario.environment_variables)
            
            # 旧的步骤执行已废弃，提示使用流程图执行
            raise Exception("请使用新的流程图执行引擎执行场景")
            
        except Exception as e:
            logger.error(f"场景执行失败: {str(e)}")
            execution.status = "failed"
            execution.error_message = str(e)
            execution.end_time = datetime.utcnow()
            if execution.start_time:
                execution.duration = (execution.end_time - execution.start_time).total_seconds()
            db.commit()
    
    async def _execute_scenario_step(self, step, context: Dict[str, Any], 
                                    db: Session) -> Dict[str, Any]:
        """执行单个场景步骤 - 已废弃"""
        return {
            "status": "failed",
            "error_message": "请使用新的流程图执行引擎",
            "error_details": "旧的步骤执行方式已废弃",
            "extracted_variables": {}
        }
    
    def _extract_variables(self, extraction_config: Dict[str, Any], response_body: str, 
                          response_headers: Dict[str, str]) -> Dict[str, str]:
        """从响应中提取变量"""
        extracted = {}
        
        try:
            for var_name, config in extraction_config.items():
                if not isinstance(config, dict):
                    continue
                
                source = config.get("source", "body")  # body, header
                extract_type = config.get("type", "jsonpath")  # jsonpath, regex, header
                expression = config.get("expression", "")
                
                if source == "header" and extract_type == "header":
                    # 从响应头提取
                    value = response_headers.get(expression, "")
                    if value:
                        extracted[var_name] = value
                
                elif source == "body" and extract_type == "jsonpath":
                    # 从JSON响应体提取
                    try:
                        response_json = json.loads(response_body)
                        jsonpath_expr = jsonpath_ng.parse(expression)
                        matches = jsonpath_expr.find(response_json)
                        if matches:
                            extracted[var_name] = str(matches[0].value)
                    except (json.JSONDecodeError, Exception) as e:
                        logger.warning(f"JSONPath提取失败: {str(e)}")
                
                elif source == "body" and extract_type == "regex":
                    # 从响应体用正则提取
                    import re
                    match = re.search(expression, response_body)
                    if match:
                        extracted[var_name] = match.group(1) if match.groups() else match.group(0)
                        
        except Exception as e:
            logger.error(f"变量提取失败: {str(e)}")
        
        return extracted


# 创建全局服务实例
api_automation_service = ApiAutomationService()